package com.tangding.service.db;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;

import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.beans.factory.annotation.Value;

import com.google.common.base.Strings;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.tangding.common.exception.GException;
import com.tangding.common.logx.LogHelp;
import com.tangding.common.tools.ErrorConstants;

/**
 * @description 
 *
 * @author Ranger
 * @version 2017年7月21日_下午2:22:29
 *
 */
public abstract class BaseMongoRepository<T, ID extends Serializable> implements MongoRepository<T, ID> {

    protected MongoDBOption option;
    protected MongoClient client;
    
    @Value("${mongo_host:127.0.0.1:27017}")
    protected String hosts;
    
    @Value("${mongo_auth:}")
    protected String auths;
	
	@SuppressWarnings("unchecked")
	Class<T> clazz = ((Class<T>)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
	
	@PostConstruct
	void init0() throws GException {
		client = initClient(hosts, auths);
		init();
		if (option == null || client == null) {
			throw new GException(ErrorConstants.ERR_INIT_DB);
		}
	}
	
	protected MongoClient initClient(String hosts, String auths) {
		if (Strings.isNullOrEmpty(hosts)) {
			LogHelp.doLogAppErr("init mongodb hosts is null!");
			System.exit(-1);
			return null;
		}
		
		List<ServerAddress> addresses = new ArrayList<>();
		List<MongoCredential> credentialsList = new ArrayList<>();
		
		String[] hostList = hosts.split("[,]");
		for (String hostPack : hostList) {
			String host = hostPack.split("[:]")[0];
			int port = Integer.parseInt(hostPack.split("[:]")[1]);
			addresses.add(new ServerAddress(host, port));
		}
		
		MongoClient client = null;
		if (Strings.isNullOrEmpty(auths)) {// 没有认证信息
			client = MongoDBUtil.createMongosClient(addresses);
		}else {
			String[] authList = auths.split("[,]");
			for (String authPack : authList) {
				String userName = authPack.split("[:]")[0];
				String password = authPack.split("[:]")[1];
				credentialsList.add(MongoCredential.createCredential(userName, "admin", password.toCharArray()));
			}
			client = MongoDBUtil.createMongosClient(addresses, credentialsList);
		}
		return client;
	}
	
	/**
	 * 需要初始化option和client 
	 */
	protected abstract void init();
	
	public T findOneById(ID id) throws GException {
        return findOne(client, option, id, clazz);
    }

    public T findOne(Bson condition) throws GException {
        return findOne(client, option, condition, clazz);
    }

    public List<T> findMany(Bson condition, int skip, int limit) throws GException {
        return findMany(client, option, condition, skip, limit, clazz);
    }
    
    public List<T> findAll() throws GException {
        return findAll(client, option, new Document(), clazz);
    }

    public List<T> findAll(Bson condition) throws GException {
        return findAll(client, option, condition, clazz);
    }
    
    public List<T> findMany(Bson condition, int skip, int limit, Bson sort) throws GException {
        return findMany(client, option, condition, skip, limit, clazz, sort);
    }

    public List<T> findAll(Bson condition, Bson sort) throws GException {
        return findAll(client, option, condition, clazz, sort);
    }

    public void deleteById(ID id) throws GException {
        delete(client, option, id);
    }

    public void delete(Bson condition) throws GException {
        delete(client, option, condition);
    }

    public void deleteMany(Bson condition) throws GException {
        deleteMany(client, option, condition);
    }

    public void save(T t) throws GException {
        save(client, option, t);
    }

    public void saveMany(List<T> ts) throws GException {
        saveMany(client, option, ts);
    }

    public void update(T t, Bson condition) {
        update(client, option, t, condition);
    }
    
    public void updateDocument(Document data, Bson condition) {
        updateDocument(client, option, data, condition);
    }
    
    public void updateMany(T t, Bson condition) {
        updateMany(client, option, t, condition);
    }
    
    public void updateDocumentMany(Document data, Bson condition) {
        updateDocumentMany(client, option, data, condition);
    }

    public void replace(T t, Bson condition) {
        replace(client, option, t, condition);
    }

    public void replaceDocument(Document data, Bson condition) {
        replaceDocument(client, option, data, condition);
    }
}
